package cn.wizzer.app.user.util;

import cn.wizzer.app.sys.modules.models.SysParam;
import cn.wizzer.framework.base.exceptions.BizException;
import cn.wizzer.framework.base.model.enums.BizExceptionEnum;
import cn.wizzer.framework.util.HttpUtil;
import cn.wizzer.framework.util.JSONUtil;
import cn.wizzer.framework.util.Result;
import com.alibaba.fastjson.JSONObject;
import org.nutz.lang.Strings;
import org.nutz.log.Log;
import org.nutz.log.Logs;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

public class OneWalletUtil {

    private static final Log log = Logs.get();

    private static SimpleDateFormat dateFormatWithZone = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss",
            Locale.getDefault());

    final static Base64.Decoder decoder = Base64.getDecoder();
    final static Base64.Encoder encoder = Base64.getEncoder();

    public static String send(String url, String body) {
        try {
            HttpUtil.Response response = HttpUtil.createRequest()
                    .setUrl(url)
                    .setBody(body)
                    .send(HttpUtil.HTTP_METHOD.POST_JSON);
            String result = response.getBodyAndClose();
            log.info("OneWalletUtil.send(url:" + url + ",params:" + body + ")->result:" + result);
            return result;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String parseRechargeParam(String bankCode, String no, BigDecimal amount, String ivKey, String cKey, String notifyUrl) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("sh_order_no", no);
        map.put("order_amount", amount.toString());
        map.put("order_type", "100");
        map.put("bank_code", bankCode);
        map.put("created_at", getCurrentDate());
        map.put("notify_url", notifyUrl);
        map.put("note", "note");
        log.info("parseRechargeParam map:"+map);
        //商户参数
        String cVal = putJsonObject(map);
        // Encrypt data
        String enString = null;
        try {
            enString = encrypt(ivKey, cVal, cKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        map.clear();
        map.put("iv", ivKey);
        map.put("value", enString);
        String finalText = putJsonObject(map);

        String dataBeforeSending = null;
        try {
            dataBeforeSending = Base64.getEncoder().encodeToString(finalText.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return dataBeforeSending;
    }

    public static String parseWithDrawParam(String bankCode, String bankAmount, String bankUserName, String no, BigDecimal amount, String ivKey, String cKey, String notifyUrl) {
        Map<String, String> map = new LinkedHashMap<>();
        map.put("sh_order_no", no);
        map.put("bank_code", bankCode);
        map.put("bank_account", bankAmount);
        map.put("bank_user_name", bankUserName);
        map.put("order_amount", amount.toString());
        map.put("notify_url", notifyUrl);
        map.put("note", "note");
        log.info("parseWithDrawParam map:"+map);
        //商户参数
        String cVal = putJsonObject(map);
        // Encrypt data
        String enString = null;
        try {
            enString = encrypt(ivKey, cVal, cKey);
        } catch (Exception e) {
            e.printStackTrace();
        }
        map.clear();
        map.put("iv", ivKey);
        map.put("value", enString);
        String finalText = putJsonObject(map);

        String dataBeforeSending = null;
        try {
            dataBeforeSending = Base64.getEncoder().encodeToString(finalText.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return dataBeforeSending;
    }

    private static String getCurrentDate() {
        return dateFormatWithZone.format(new Date());
    }

    private static String putJsonObject(Map<String, String> map) {
        return JSONUtil.toJSONString(map);
    }

    // Encrypt
    private static String encrypt(String ivKey, String sVal, String sKey) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(decoder.decode(sKey), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(decoder.decode(ivKey));
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        return encoder.encodeToString(cipher.doFinal(sVal.getBytes()));
    }

    // Decrypt
    public static String decrypt(String respBody, SysParam sysParam) throws Exception {
        JSONObject jsonObject = JSONObject.parseObject(new String(decoder.decode(respBody), "UTF-8"));
        String ivKey = (String) jsonObject.get("iv");
        SecretKeySpec skeySpec = new SecretKeySpec(decoder.decode(sysParam.getOneWalletSafeCode()), "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        IvParameterSpec iv = new IvParameterSpec(decoder.decode(ivKey));
        cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
        byte[] original = cipher.doFinal(decoder.decode((String) jsonObject.get("value")));
        String result = new String(original);
        jsonObject = JSONObject.parseObject(result);
        if (Strings.isNotBlank(jsonObject.getString("error_code")) && !jsonObject.getInteger("error_code").equals(200)) {
            if (sysParam.isDebug())
                throw new BizException((String) ((JSONObject) jsonObject.get("data")).get("message"));
            log.error("oneWalletError"+jsonObject.toJSONString());
            Result.breakWith(BizExceptionEnum.RETRY);
        }
        return result;
    }

    public static String randomByte() {
        byte[] b = new byte[16];
        new Random().nextBytes(b);
        return encoder.encodeToString(b);
    }


    public static String success(String sKey) throws Exception {
        Map<String, String> map = Result.createHashMap("result", "success");
        String ivKey = randomByte();
        String enString = encrypt(ivKey, putJsonObject(map), sKey);
        map.put("iv", ivKey);
        map.put("value", enString);
        String finalText = putJsonObject(map);

        String dataBeforeSending = null;
        try {
            dataBeforeSending = Base64.getEncoder().encodeToString(finalText.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return dataBeforeSending;
    }
}
